﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using AForge.Video;
using AForge.Video.DirectShow;
using System.Drawing;
using System.Runtime.InteropServices;
using System.IO;
using System.Drawing.Imaging;

namespace udp_camera_client
{
    //client sends images to server, fuck you
    public class VidCam
    {
        private static FilterInfoCollection videoDevices;
        private static VideoCaptureDevice videoDevice;
        private static VideoInput[] availableVideoInputs = null;
        private static string videoDeviceMoniker;
        private static VideoCapabilities caps;
        private const int chunckSize = 500;
        private static int jpegNumber = 0;
        static Udp udp;

        public static void initialize(int cameraNumber, int resolutionNumber, string ip, int port)
        {


            udp = new Udp(ip, port);
          
            
            videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
            videoDeviceMoniker = videoDevices[cameraNumber].MonikerString.ToString();
            
            videoDevice = new VideoCaptureDevice(videoDeviceMoniker);
            VideoCapabilities[] videoCapabilities = videoDevice.VideoCapabilities;

            caps = videoCapabilities[resolutionNumber];

            
            videoDevice.VideoResolution = caps;
            videoDevice.NewFrame += new NewFrameEventHandler(handleFrame);
            videoDevice.Start();

        }

        private static ImageCodecInfo GetEncoder(ImageFormat format)
        {
            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
            foreach (ImageCodecInfo codec in codecs)
            {
                if (codec.FormatID == format.Guid)
                {
                    return codec;
                }
            }
            return null;
        }

        public static void handleFrame(object sender, NewFrameEventArgs eventArgs)
        {
            
            Bitmap frame = (Bitmap)eventArgs.Frame.Clone();
            
            //testing jpeg quality levels
            //http://msdn.microsoft.com/en-us/library/system.drawing.imaging.encoderparameter.aspx
           // ImageCodecInfo jgpEncoder = GetEncoder(ImageFormat.Jpeg);


           // System.Drawing.Imaging.Encoder myEncoder = System.Drawing.Imaging.Encoder.Quality;
           // EncoderParameters myEncoderParameters = new EncoderParameters(1);

           ////quality set here 
           // EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, 100L);
           // myEncoderParameters.Param[0] = myEncoderParameter;
            
           // frame.Save(@"C:\Users\Icarus\Desktop\res_index4\pic100.jpeg", jgpEncoder, myEncoderParameters);



           // myEncoderParameter = new EncoderParameter(myEncoder, 75L);
           // myEncoderParameters.Param[0] = myEncoderParameter;

           // frame.Save(@"C:\Users\Icarus\Desktop\res_index4\pic75.jpeg", jgpEncoder, myEncoderParameters);




           // myEncoderParameter = new EncoderParameter(myEncoder, 50L);
           // myEncoderParameters.Param[0] = myEncoderParameter;

           // frame.Save(@"C:\Users\Icarus\Desktop\res_index4\pic50.jpeg", jgpEncoder, myEncoderParameters);





           // myEncoderParameter = new EncoderParameter(myEncoder, 25L);
           // myEncoderParameters.Param[0] = myEncoderParameter;

           // frame.Save(@"C:\Users\Icarus\Desktop\res_index4\pic25.jpeg", jgpEncoder, myEncoderParameters);




           // myEncoderParameter = new EncoderParameter(myEncoder, 0L);
           // myEncoderParameters.Param[0] = myEncoderParameter;

           // frame.Save(@"C:\Users\Icarus\Desktop\res_index4\pic0.jpeg", jgpEncoder, myEncoderParameters);
           // frame.Save(@"C:\Users\Icarus\Desktop\pic0.bmp", ImageFormat.Bmp);
           // //


            
            MemoryStream ms = new MemoryStream();
            frame.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
            makeChucks(ms);
            
        }

        private static void makeChucks(MemoryStream ms)
        {
            ms.Seek(0, 0);
            byte[] data = new byte[ms.Length];
            ms.Read(data, 0, data.Length);
            //chunckSize bytes is how many bytes will be sent in one UDP frame
            byte[] udp_frame = new byte[chunckSize];
            int length;
            int chuncks = (data.Length / chunckSize) + 1;
            for (int k = 0; k < chuncks; k++)
            {

                if ((data.Length - (k) * chunckSize) < chunckSize)
                {
                    length = data.Length % chunckSize;
                    byte[] udp_frame_last = new byte[length];
                    Array.Copy(data, k * chunckSize, udp_frame_last, 0, length);
                    constructMetaDataAndSend(udp_frame_last, k, data.Length);

                }
                else
                {
                    length = chunckSize;
                    Array.Copy(data, k * chunckSize, udp_frame, 0, length);
                    constructMetaDataAndSend(udp_frame, k, data.Length);
                }
                 
                 
                
                
            }

            jpegNumber++;
        }

        private static void constructMetaDataAndSend(byte[] data, int k, int size)
        {
            const int headerSize_TotalExpectedUDP = 4;
            const int headerSize_ThisFrame = 4;
            const int headerSize_JPEGnum = 4;

            int frame_num = new int();
            frame_num = k;
            byte[] totalJPEGSize = BitConverter.GetBytes(size);
            byte[] thisFrameNum = BitConverter.GetBytes(frame_num);
            byte[] JPEGnum = BitConverter.GetBytes(jpegNumber);

            byte[] final_udp_frame = new byte[data.Length + headerSize_TotalExpectedUDP + headerSize_ThisFrame + headerSize_JPEGnum];

            Array.Copy(JPEGnum, final_udp_frame, JPEGnum.Length);
            Array.Copy(totalJPEGSize, 0, final_udp_frame, 4, totalJPEGSize.Length);
            Array.Copy(thisFrameNum, 0, final_udp_frame, 8, thisFrameNum.Length);
            Array.Copy(data, 0, final_udp_frame, 12, data.Length);

            udp.udpServer.sendingPart(final_udp_frame);
        }


    }

 

    public class Udp
    {
        public UdpImageClient udpServer;
        public Udp(string ip, int port)
        {
            udpServer= new UdpImageClient(ip, port);
        }
    }

    //this class is for the receiving end
    

    public class UdpImageClient
    {
        private int port;
        private string hostname;
        ASCIIEncoding ascii;
        UdpClient server;
        IPEndPoint send;

        public UdpImageClient(string hostname, int port)
        {
            this.port = port;
            this.hostname = hostname;
            server = new UdpClient(this.hostname, this.port);
            send = new IPEndPoint(IPAddress.Any, 0);
        }

        public void sendingPart(byte[] data)
        {
            server.Send(data, data.Length);
        }
    }

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            Console.WriteLine("Cam index kthx");
            int camIndex = int.Parse(Console.ReadLine());

            Console.WriteLine("Resolution index kthx");
            int resIndex = int.Parse(Console.ReadLine());

            Console.WriteLine("IP plz kthx");
            string ip = (Console.ReadLine());

            Console.WriteLine("Port plz kthx");
            int port = int.Parse((Console.ReadLine()));

            InitializeComponent();
            VidCam.initialize(camIndex, resIndex, ip, port);
            
        }
    }
}
